#ifndef decoder_h
#define decoder_h

#include "frame.c"
#include "libavcodec/avcodec.h"
#include "packet.c"
#include <SDL.h>
#include <stdint.h>

typedef struct Decoder
{
  AVPacket        pkt;
  PacketQueue*    queue;
  AVCodecContext* avctx;
  int             pkt_serial;
  int             finished;
  int             packet_pending;
  SDL_cond*       empty_queue_cond;
  int64_t         start_pts;
  AVRational      start_pts_tb;
  int64_t         next_pts;
  AVRational      next_pts_tb;
  SDL_Thread*     decoder_tid;
} Decoder;

void decoder_init(Decoder* d, AVCodecContext* avctx, PacketQueue* queue, SDL_cond* empty_queue_cond);
int  decoder_start(Decoder* d, int (*fn)(void*), const char* thread_name, void* arg);
int  decoder_decode_frame(Decoder* d, AVFrame* frame, AVSubtitle* sub);
void decoder_abort(Decoder* d, FrameQueue* fq);
void decoder_destroy(Decoder* d);

#endif

#if __INCLUDE_LEVEL__ == 0

#include "packet.c"
#include <SDL.h>

static int decoder_reorder_pts = -1;

void decoder_init(Decoder* d, AVCodecContext* avctx, PacketQueue* queue, SDL_cond* empty_queue_cond)
{
  memset(d, 0, sizeof(Decoder));
  d->avctx            = avctx;
  d->queue            = queue;
  d->empty_queue_cond = empty_queue_cond;
  d->start_pts        = AV_NOPTS_VALUE;
  d->pkt_serial       = -1;
}

int decoder_start(Decoder* d, int (*fn)(void*), const char* thread_name, void* arg)
{
  packet_queue_start(d->queue);
  d->decoder_tid = SDL_CreateThread(fn, thread_name, arg);
  if (!d->decoder_tid)
  {
    av_log(NULL, AV_LOG_ERROR, "SDL_CreateThread(): %s\n", SDL_GetError());
    return AVERROR(ENOMEM);
  }
  return 0;
}

int decoder_decode_frame(Decoder* d, AVFrame* frame, AVSubtitle* sub)
{
  int ret = AVERROR(EAGAIN);

  for (;;)
  {
    AVPacket pkt;

    if (d->queue->serial == d->pkt_serial)
    {
      do
      {
        if (d->queue->abort_request)
          return -1;

        switch (d->avctx->codec_type)
        {
        case AVMEDIA_TYPE_VIDEO:
          ret = avcodec_receive_frame(d->avctx, frame);
          if (ret >= 0)
          {
            if (decoder_reorder_pts == -1)
            {
              frame->pts = frame->best_effort_timestamp;
            }
            else if (!decoder_reorder_pts)
            {
              frame->pts = frame->pkt_dts;
            }
          }
          break;
        case AVMEDIA_TYPE_AUDIO:
          ret = avcodec_receive_frame(d->avctx, frame);
          if (ret >= 0)
          {
            AVRational tb = (AVRational){1, frame->sample_rate};
            if (frame->pts != AV_NOPTS_VALUE)
              frame->pts = av_rescale_q(frame->pts, d->avctx->pkt_timebase, tb);
            else if (d->next_pts != AV_NOPTS_VALUE)
              frame->pts = av_rescale_q(d->next_pts, d->next_pts_tb, tb);
            if (frame->pts != AV_NOPTS_VALUE)
            {
              d->next_pts    = frame->pts + frame->nb_samples;
              d->next_pts_tb = tb;
            }
          }
          break;
        default:
          break;
        }
        if (ret == AVERROR_EOF)
        {
          d->finished = d->pkt_serial;
          avcodec_flush_buffers(d->avctx);
          return 0;
        }
        if (ret >= 0)
          return 1;
      } while (ret != AVERROR(EAGAIN));
    }

    do
    {
      if (d->queue->nb_packets == 0)
        SDL_CondSignal(d->empty_queue_cond);
      if (d->packet_pending)
      {
        av_packet_move_ref(&pkt, &d->pkt);
        d->packet_pending = 0;
      }
      else
      {
        if (packet_queue_get(d->queue, &pkt, 1, &d->pkt_serial) < 0)
          return -1;
      }
      if (d->queue->serial == d->pkt_serial)
        break;
      av_packet_unref(&pkt);
    } while (1);

    if (pkt.data == flush_pkt.data)
    {
      avcodec_flush_buffers(d->avctx);
      d->finished    = 0;
      d->next_pts    = d->start_pts;
      d->next_pts_tb = d->start_pts_tb;
    }
    else
    {
      if (d->avctx->codec_type == AVMEDIA_TYPE_SUBTITLE)
      {
        int got_frame = 0;
        ret           = avcodec_decode_subtitle2(d->avctx, sub, &got_frame, &pkt);
        if (ret < 0)
        {
          ret = AVERROR(EAGAIN);
        }
        else
        {
          if (got_frame && !pkt.data)
          {
            d->packet_pending = 1;
            av_packet_move_ref(&d->pkt, &pkt);
          }
          ret = got_frame ? 0 : (pkt.data ? AVERROR(EAGAIN) : AVERROR_EOF);
        }
      }
      else
      {
        if (avcodec_send_packet(d->avctx, &pkt) == AVERROR(EAGAIN))
        {
          av_log(d->avctx, AV_LOG_ERROR, "Receive_frame and send_packet both returned EAGAIN, which is an API violation.\n");
          d->packet_pending = 1;
          av_packet_move_ref(&d->pkt, &pkt);
        }
      }
      av_packet_unref(&pkt);
    }
  }
}

void decoder_abort(Decoder* d, FrameQueue* fq)
{
  packet_queue_abort(d->queue);
  frame_queue_signal(fq);
  SDL_WaitThread(d->decoder_tid, NULL);
  d->decoder_tid = NULL;
  packet_queue_flush(d->queue);
}

void decoder_destroy(Decoder* d)
{
  av_packet_unref(&d->pkt);
  avcodec_free_context(&d->avctx);
}

#endif
